#include "ntddk.h"

struct PhysicalMemoryRun {
	ULONG_PTR base_page;   //!< A base address / PAGE_SIZE (ie, 0x1 for 0x1000)
	ULONG_PTR page_count;  //!< A number of pages
};

struct PhysicalMemoryDescriptor {
	PFN_COUNT number_of_runs;    //!< A number of PhysicalMemoryDescriptor::run
	PFN_NUMBER number_of_pages;  //!< A physical memory size in pages
	PhysicalMemoryRun run[1];    //!< ranges of addresses
};

enum class VmxStatus : unsigned __int8 {
	kOk = 0,                  //!< Operation succeeded
	kErrorWithStatus = 1,     //!< Operation failed with extended status available
	kErrorWithoutStatus = 2,  //!< Operation failed without status available
};

PFN_NUMBER UtilPfnFromPa(ULONG64 pa);
NTSTATUS UtilInitialization(PDRIVER_OBJECT driver_object);
NTSTATUS UtilForEachProcessor(NTSTATUS(*callback_routine)(void *), void *context);
void UtilFreeContiguousMemory(void *base_address);
void *UtilVaFromPfn(PFN_NUMBER pfn);
void *UtilVaFromPa(ULONG64 pa);
ULONG64 UtilPaFromPfn(PFN_NUMBER pfn);
void *UtilVaFromPfn(PFN_NUMBER pfn);
void *UtilAllocateContiguousMemory(SIZE_T number_of_bytes);
ULONG_PTR UtilGetGlobalPteAddress();

ULONG64 UtilPaFromVa(_In_ void *va);

VmxStatus UtilInveptGlobal();
VmxStatus UtilInvvipAllContext();

bool UtilIsX86Pae();

template <typename T>
bool UtilIsInBounds(_In_ const T &value, _In_ const T &min, _In_ const T &max) {
	return (min <= value) && (value <= max);
}
PhysicalMemoryDescriptor *UtilGetPhysicalMemoryRanges();

VmxStatus UtilInvvpidAllContext();
VmxStatus UtilInvvpidIndividualAddress(USHORT vpid, void *address);
VmxStatus UtilInvvpidSingleContextExceptGlobal(USHORT vpid);